package com.yunc.modules;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yunc.base.aspect.AuthIgnore;
import com.yunc.base.aspect.DataSource;
import com.yunc.base.aspect.DataSourceType;
import com.yunc.base.service.OkHttpService;
import com.yunc.base.service.RedisService;
import com.yunc.base.utils.JwtApiTokenUtils;
import com.yunc.base.utils.R;
import com.yunc.base.utils.SnowFlake;
import com.yunc.modules.shop.entity.*;
import com.yunc.modules.shop.entity.converter.ShopConverter;
import com.yunc.modules.shop.entity.dto.ShopDTO;
import com.yunc.modules.shop.service.*;
import com.yunc.modules.sys.entity.SysUser;
import com.yunc.modules.sys.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author taocongcong
 * @create 2019-11-06 14:53
 */
@RestController
@RequestMapping("/api")
@Slf4j
public class ApiController {

    private static final SnowFlake SNOW_FLAKE = new SnowFlake(9,9);
    private static final Snowflake snowflake = IdUtil.createSnowflake(1, 1);

    @Autowired
    private ISysUserService sysUserService;
    @Autowired
    private IShopBannerService shopBannerService;
    @Autowired
    private IShopMemberService shopMemberService;
    @Autowired
    private IShopOrderService shopOrderService;
    @Autowired
    private IShopItemSpecsService shopItemSpecsService;
    @Autowired
    private IShopItemService shopItemService;
    @Autowired
    private IShopRandomCodeService shopRandomCodeService;
    @Autowired
    private RedisService redisService;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private OkHttpService okHttpService;
    @Autowired
    @Qualifier("taskExecutor")
    private ThreadPoolTaskExecutor taskExecutor;

    private static final String ITEM_QUEUE = "ITEM_QUEUE";

    @AuthIgnore
    @PostMapping("/login")
    public R memberLogin(String phone, String password){
        if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(password)) {
            return R.error("手机号或密码为空");
        }
        boolean phoneFlag = Validator.isMobile(phone);
        if (!phoneFlag) {
            return R.error("登录手机号格式不正确");
        }
        // 使用md5加密
        ShopMember member = shopMemberService.getOne(new QueryWrapper<ShopMember>().eq("phone", phone));
        if (member == null) {
            return R.error("无法查询到用户");
        }
        String passwordMd5 = DigestUtil.md5Hex(password);
        if (!StringUtils.equals(member.getPassword(), passwordMd5)) {
            return R.error("密码错误");
        }
        log.info("登录手机号：{}，于{}时间登录成功", phone, DateUtil.formatDateTime(new Date()));
        String token = JwtApiTokenUtils.createToken(String.valueOf(member.getId()), member.getNickName(), true);
        return R.ok().put("token", token);
    }

    @AuthIgnore
    @GetMapping("/userList")
    public R userList(){
        List<SysUser> sysUserList = sysUserService.list(null);
        return R.ok().put("sysUserList", sysUserList);
    }

    @GetMapping("/shopBannerList")
    @Cacheable(value = "shopBannerByTypeList")
    @DataSource(value = DataSourceType.SHOP)
    public R shopBannerList(String type){
        List<ShopBanner> shopBannerList = shopBannerService.list(new QueryWrapper<ShopBanner>().eq("type", type));
        return R.ok().put("shopBannerList", shopBannerList);
    }

    @GetMapping("/userPage")
    public R userPage(){
        List<SysUser> sysUserList = sysUserService.list(null);
        return R.ok().put("sysUserList", sysUserList);
    }

    @GetMapping("/getUserByToken")
    public R getUserByToken(){
        String token = JwtApiTokenUtils.getToken();
        String userId = JwtApiTokenUtils.getUserId(token);
        String userName = JwtApiTokenUtils.getUserName(token);
        JwtApiTokenUtils.clearToken();
        log.info("查询当前线程用户：{}", userName);
        return R.ok().put("userId", userId).put("userName", userName);
    }

    /**
     * 生成订单
     * 使用Redis分布式锁控制并发，默认购买一个
     */
    @PostMapping("/createOrder")
    public R createOrder(String itemId, String specsSeq){
        // 如果采用分布式锁呢
        RLock rlock = redissonClient.getLock("redisson:lock:itemspecsseq:c");
        // 设置锁超时时间，防止异常造成死锁
        rlock.lock(10, TimeUnit.SECONDS);
        try {
            // 执行业务逻辑
            ShopItemSpecs shopItemSpecs = shopItemSpecsService.getById(Long.valueOf(specsSeq));
            if (shopItemSpecs.getStock() <= 0) {
                log.info("商品SKU编号{}，库存不足", specsSeq);
                return R.error("库存不足");
            }
            shopItemSpecs.setStock(shopItemSpecs.getStock() - 1);
            log.info("将队列中信息取出，保存至数据库，数据库库存为：{}", shopItemSpecs.getStock());
            shopItemSpecsService.updateById(shopItemSpecs);
        } catch(Exception e){
            e.printStackTrace();
        } finally{
            rlock.unlock();
        }
        ShopRandomCode shopRandomCode = new ShopRandomCode();
        String orderNumber = String.valueOf(SNOW_FLAKE.nextId());
        shopRandomCode.setCode(orderNumber);
        // 生成订单，生成订单按订单号传值给前端
        // 使用多线程处理，不及时反馈到前端
        shopRandomCodeService.save(shopRandomCode);
        return R.ok();
    }

    @AuthIgnore
    @GetMapping("/initShopStock")
    public R initShopStock(int stock){
        String key = "ITEM_SPECSSEQ_81";
        redisService.set(key, stock);
        return R.ok().put("key", key);
    }

    /**
     * 生成订单
     * 使用Redis控制并发，默认购买一个
     */
    @PostMapping("/createOrderByRedis")
    // @Limit(50)
    public R createOrderByRedis(String itemId, String specsSeq){
        String currentUserId = JwtApiTokenUtils.getCurrentUserId();
        if (StringUtils.isNotEmpty(specsSeq)) {
            // 改变库存值
            String key = "ITEM_SPECSSEQ_" + specsSeq;
            long decr = redisService.decr(key, 1);
            // 判断库存数
            if (decr >= 0) {
                log.info("在Redis中查询商品SKU为{}，减去缓存，现有库存数为：{}", key, decr);
                // 发送消息减去库存，不能使用并发减去库存，不然依然存在并发
                // 队列少了怎么回事
                // String orderNumber = String.valueOf(SNOW_FLAKE.nextId());
                // amqpTemplate.convertAndSend(ITEM_QUEUE, currentUserId + ","+ specsSeq + "," + orderNumber);
                // 不使用队列，直接开多线程保存
                String orderNumber = snowflake.nextIdStr();
                taskExecutor.execute(() -> {
                    log.info("保存至数据库，订单号为：{}，数据库库存为：{}", orderNumber, decr);
                    ShopRandomCode shopRandomCode = new ShopRandomCode();
                    shopRandomCode.setCode(orderNumber);
                    shopRandomCode.setUserId(currentUserId);
                    shopRandomCodeService.save(shopRandomCode);
                });

            } else {
                long incr = redisService.incr(key, 1);
                // 需要下架该商品1、如果该商品有缓存则需要去除，2、将该商品数据库中状态修改，放入消息，或者使用多线程去处理
                log.info("您选择的商品{}，库存不足", key);
                return R.error("您选择的商品库存不足").put("itemCount", incr);
            }
        } else {
            log.info("在Redis中查询商品{}，减去缓存", "ITEM_" + itemId);
            long decr = redisService.decr("ITEM_" + itemId, 1);
            // 判断库存数
            if (decr >= 0) {
                // 发送消息减去库存
                // amqpTemplate.convertAndSend(ITEM_QUEUE, itemId);
            } else {
                long incr = redisService.incr(itemId, 1);
                // 需要下架该商品1、如果该商品有缓存则需要去除，2、将该商品数据库中状态修改，放入消息，或者使用多线程去处理
                return R.error("您选择的商品库存不足").put("itemCount", incr);
            }
        }
        // 生成订单，生成订单按订单号传值给前端

        // 使用多线程处理，不及时反馈到前端
        return R.ok();
    }

    /**
     * 生成唯一性订单号
     */
    @AuthIgnore
    @PostMapping("/generateShopNumber")
    public R generateShopNumber(){
        ShopRandomCode entity = new ShopRandomCode();
        // 采用雪花算法生成订单编号
        String code = String.valueOf(SNOW_FLAKE.nextId());
        entity.setCode(code);
        shopRandomCodeService.save(entity);
        log.info("生成唯一性订单号：{}", code);
        return R.ok().put("code", code);
    }

    @AuthIgnore
    @GetMapping("/getOpenApiToken")
    public R getOpenApiToken(){
        Map<String, String> map = new HashMap<>();
        map.put("appCode", "15a4f322-06eb-4ea5-b362-9ae284b2cdbf");
        map.put("appPass", "5ddd6c0541364f2dbb7001ee79252a64");
        String url = "http://117.71.58.71:666/auth/getOpenApiToken";
        String s = okHttpService.get(url, map);
        return R.ok().put("s", s);
    }

    @AuthIgnore
    @GetMapping("/shopToDTO")
    public R shopToDTO(){
        Shop shop = new Shop();
        shop.setId("1");
        shop.setSn("1213");
        shop.setUserId("adsds");
        ShopDTO shopDTO = ShopConverter.INSTANCE.domain2dto(shop);
        return R.ok().put("data", shopDTO);
    }

    @AuthIgnore
    @GetMapping("/gcc")
    public R gcc(){
        System.gc();
        return R.ok();
    }
}
